#pragma once

#include "./channel.hpp"
#include "../mqcommon/msg.pb.h"
#include "../mqcommon/proto.pb.h"

namespace mymq
{
    class Connection
    {
    public:
        using ptr = std::shared_ptr<Connection>;
    public:
        Connection(const ConsumerManager::ptr& cmp,
                    const VirtualHost::ptr host, 
                    const muduo::net::TcpConnectionPtr conn, const ProtobufCodecPtr& codec,
                    const ThreadPool::ptr pool)
        : _consumer_manager(cmp)
        , _host(host)
        , _conn(conn)
        , _codec(codec)
        , _pool(pool)
        {}
        ~Connection() {}
        void CreateChannel(const OpenChannelRequestPtr& req)
        {
            // 创建信道，判断信道id是否重复
            bool ret = _channel_manager.CreateChannel(req->cid(), _consumer_manager, _host, _conn, _codec, _pool);
            Response(req->rid(), req->cid(), ret);
        }
        void DestoryChannel(const CloseChannelRequestPtr& req)
        {
            // 删除信道，判断信道id是否没有
            bool ret = _channel_manager.DestoryChannel(req->cid());
            Response(req->rid(), req->cid(), ret);
        }
        Channel::ptr SelectChannel(const std::string& cid)
        {
            return _channel_manager.SelectChannel(cid);
        }
    private:
        void Response(const std::string rid, const std::string cid, bool ok)
        {
            CommonResponse resp;
            resp.set_rid(rid);
            resp.set_cid(cid);
            resp.set_ok(ok);
            _codec->send(_conn, resp);
        }
    private:
        ConsumerManager::ptr _consumer_manager;
        VirtualHost::ptr _host;
        muduo::net::TcpConnectionPtr _conn;
        ProtobufCodecPtr _codec;
        ThreadPool::ptr _pool;
        ChannelManager _channel_manager;
    };

    class ConnectionManager
    {
    public:
        using ptr = std::shared_ptr<ConnectionManager>;
    public:
        ConnectionManager() {}
        void NewConnection(const ConsumerManager::ptr& cmp,
                    const VirtualHost::ptr host, 
                    const muduo::net::TcpConnectionPtr conn, const ProtobufCodecPtr& codec,
                    const ThreadPool::ptr pool)
        {
            std::lock_guard<std::mutex> lock(_mtx);
            auto it = _conns.find(conn);
            if (it != _conns.end())
            {
                return;
            }
            Connection::ptr conn_ = std::make_shared<Connection>(cmp, host, conn, codec, pool);
            _conns.insert(std::make_pair(conn, conn_));
        }
        Connection::ptr GetConnection(const muduo::net::TcpConnectionPtr conn)
        {
            std::lock_guard<std::mutex> lock(_mtx);
            auto it = _conns.find(conn);
            if (it == _conns.end())
                return Connection::ptr();
            return it->second;
        }
        void DeleteConnection(const muduo::net::TcpConnectionPtr conn)
        {
            std::lock_guard<std::mutex> lock(_mtx);
            _conns.erase(conn);
        }
    private:
        std::mutex _mtx;
        std::unordered_map<muduo::net::TcpConnectionPtr, Connection::ptr> _conns;
    };
}